package com.roin.learn.activiti.controller;

import com.alibaba.fastjson.JSON;
import com.github.pagehelper.util.StringUtil;
import com.roin.learn.activiti.base.BaseController;
import com.roin.learn.activiti.base.SysConstent;
import com.roin.learn.activiti.command.ProcessInstanceDiagramCmd;
import com.roin.learn.activiti.command.UpdateProcessCmd;
import com.roin.learn.activiti.entity.ResourceResult;
import com.roin.learn.activiti.entity.User;
import com.roin.learn.activiti.exception.BusinessException;
import com.roin.learn.activiti.service.ProcessService;
import com.roin.learn.activiti.service.UserService;
import com.roin.learn.activiti.utils.IoUtils;
import org.activiti.engine.*;
import org.activiti.engine.impl.cmd.GetDeploymentProcessDiagramCmd;
import org.activiti.engine.impl.cmd.SetProcessInstanceNameCmd;
import org.activiti.engine.impl.interceptor.Command;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.commons.CommonsMultipartFile;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

/**
 * 流程定义业务Controller
 */
@Controller
@RequestMapping("process/defintion")
public class ProcessDefinitionController extends BaseController{
    @Resource
    ProcessService processService;
    @Resource
    ManagementService managementService;
    @Resource
    RepositoryService repositoryService;
    @Resource
    RuntimeService runtimeService;
    @Resource
    HistoryService historyService;
    @Resource
    IdentityService identityService;
    @Resource
    FormService formService;
    /**
     * 转到流程定义列表页面
     * @return
     */
    @RequestMapping("")
    public ModelAndView toDefintion(){
        ModelAndView mav = new ModelAndView("process/defintion");
        List<ProcessDefinition> defintions=processService.findProcessDefintion(null,null,null,null,null);
        mav.addObject("defintions",defintions);
        return mav;
    }

    /**
     *挂起或激活流程
     * @param processDefinitionId 流程定义ID
     * @param suspendOrActivate 挂起还是激活，suspend挂起，activate激活
     * @return
     */
    @RequestMapping("suspendOrActivate.do")
    @ResponseBody
    public ResourceResult suspendOrActivate(String processDefinitionId,String suspendOrActivate){
        ResourceResult result = new ResourceResult();
        try {
            checkLogin();
            if ("suspend".equals(suspendOrActivate)){
                if (repositoryService.isProcessDefinitionSuspended(processDefinitionId)){
                    throw new BusinessException("此流程已为挂起状态，不需要挂起！");
                }else {
                    repositoryService.suspendProcessDefinitionById(processDefinitionId);
                    result.setSuccess(true).setMessage("挂起成功！");
                }
            }
            else if ("activate".equals(suspendOrActivate)){
                if (repositoryService.isProcessDefinitionSuspended(processDefinitionId)){
                    repositoryService.activateProcessDefinitionById(processDefinitionId);
                    result.setSuccess(true).setMessage("激活成功！");
                }
                else
                    throw new BusinessException("此流程已为激活状态，不需要激活！");
            }
            else {
                throw new BusinessException("suspendOrActivate必须为'suspend'和'activate'中的一个！");
            }

        } catch (BusinessException e) {
            result.setMessage(e.getMessage());
        } catch (Exception e){
            log.error("挂起流程异常",e);
            result.setMessage(SysConstent.MSG_SYS_ERROR);
        }
        return result;
    }

    /**
     * 查看流程定义XML
     * @param processDefinitionId
     * @param response
     */
    @RequestMapping("viewXml.htm")
    public void viewXml(@RequestParam("processDefinitionId") String processDefinitionId,
                        HttpServletResponse response){
        try {
            ProcessDefinition processDefinition = repositoryService
                    .createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId).singleResult();
            String resourceName = processDefinition.getResourceName();
            InputStream resourceAsStream = repositoryService.getResourceAsStream(
                    processDefinition.getDeploymentId(), resourceName);
            response.setContentType("text/xml;charset=UTF-8");
            IOUtils.copy(resourceAsStream, response.getOutputStream());
        } catch (IOException e) {
            log.error("查看流程定义Xml异常",e);
        }
    }

    /**
     * 获取流程定义Xml
     * @param processDefinitionId
     * @return
     */
    @RequestMapping("getXml.do")
    @ResponseBody
    public ResourceResult getXml(@RequestParam("processDefinitionId") String processDefinitionId){
        ResourceResult result = new ResourceResult();
        try {
            ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);
            InputStream is = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                    processDefinition.getResourceName());
            String xml = IoUtils.readString(is);
            result.setSuccess(true).setData(xml);
            return result;
        } catch (IOException e) {
            log.error("获取流程定义Xml异常",e);
        }
        return result;
    }

    /**
     * 更新流程，不生成新版本.
     * @param processDefinitionId
     * @param xml
     * @return
     */
    @RequestMapping("updateXml.do")
    @ResponseBody
    public ResourceResult updateXml(@RequestParam("processDefinitionId") String processDefinitionId,
                                    @RequestParam("xml") String xml){
        ResourceResult result = new ResourceResult();
        try {
            byte[] bytes = xml.getBytes("utf-8");
            UpdateProcessCmd updateProcessCmd = new UpdateProcessCmd(processDefinitionId, bytes);
            managementService.executeCommand(updateProcessCmd);
            result.setSuccess(true);
            result.setMessage("更新成功！");
        } catch (UnsupportedEncodingException e) {
            log.error("更新流程定义Xml异常！",e);
            result.setMessage(SysConstent.MSG_SYS_ERROR);
        }
        return result;
    }

    /**
     * 通过文件发布流程定义
     * @param name
     * @param xml
     * @return
     */
    @RequestMapping("add.do")
    @ResponseBody
    public ResourceResult add(String name, @RequestParam(required = false) String xml, @RequestParam(required = false) CommonsMultipartFile zipFile) {
        ResourceResult result = new ResourceResult();
        try {
            if (StringUtil.isEmpty(xml) && (zipFile==null || zipFile.isEmpty())) {
                result.setMessage("添加失败!xml参数和file参数不能都为空！");
                return result;
            }
            if (!StringUtil.isEmpty(xml)){
                Deployment deployment = processService.deployString(xml,name);

                result.setSuccess(deployment!=null).setMessage(result.isSuccess() ? "添加成功！" : "添加失败!");
                return result;
            }
            if (zipFile!=null) {
                ZipInputStream inputStream=new ZipInputStream(zipFile.getInputStream());
                Deployment deployment = processService.deployZipInputStream(inputStream,name);
                result.setSuccess(deployment!=null).setMessage(result.isSuccess() ? "添加成功！" : "添加失败!");
                return result;
            }
        } catch (IOException e) {
            log.error("通过文件发布流程定义异常",e);
            result.setMessage(SysConstent.MSG_SYS_ERROR);
        }
        return result;
    }

    /**
     * 发起流程
     * @param key
     * @return
     */
    @RequestMapping("start.do")
    @ResponseBody
    public ResourceResult start(String key, HttpSession session) {
        ResourceResult result = new ResourceResult();
        try {
            User user = checkLogin(session);
            if (StringUtil.isEmpty(key))
                result.setMessage( "发起失败!key参数为空！");
            if (StringUtil.isEmpty(result.getMessage())) {
                try {
                    //设置流程发起人
                    identityService.setAuthenticatedUserId(user.getUserId());
                    Map<String,Object> variables = new HashMap<>();
                    variables.put("positionId",user.getPositionId().toString());
                    variables.put("userId",user.getUserId());
                    variables.put("userName",user.getUserName());
                    variables.put("userEmail",user.getUserEmail());
                    variables.put("positionMc",user.getPositionMc());
//                    variables.put("user",user);
                    ProcessInstance instance = processService.startProessInstance(key, null, variables);
                    result.setSuccess(instance!=null);
                    result.setMessage(result.isSuccess() ? "发起成功！" : "发起失败!");
                    try {
                        String name = String.format("%s的%s流程实例",user.getUserName(),instance.getProcessDefinitionName());
                        managementService.executeCommand(new SetProcessInstanceNameCmd(instance.getProcessInstanceId(),name));
                    } catch (Exception e) {
                        log.error("发起流程设置流程实例名称异常",e);
                    }
                }catch (Exception e){
                    log.error("发起流程出错",e);
                    result.setMessage("发起失败!");
                }

            }
        } catch (BusinessException e) {
            result.setMessage(e.getMessage());
        }
        return result;
    }

    /**
     * 通过流程启动表单启动流程
     * @param id 流程定义ID
     * @param properties 表单属性值json字符串
     * @param session
     * @return
     */
    @RequestMapping("startByFormData.do")
    @ResponseBody
    public ResourceResult startByFormData(String id, String properties, HttpSession session) {
        ResourceResult result = new ResourceResult();
        try {
            User user = checkLogin(session);
            Map formProperties = JSON.parseObject(properties,Map.class);
            try {
                //设置流程发起人
                identityService.setAuthenticatedUserId(user.getUserId());
                formProperties.put("positionId",user.getPositionId().toString());
                formProperties.put("userId",user.getUserId());
                formProperties.put("userName",user.getUserName());
                formProperties.put("userEmail",user.getUserEmail());
                formProperties.put("positionMc",user.getPositionMc());
//                formProperties.put("user",user);
                ProcessInstance instance = formService.submitStartFormData(id, formProperties);
                result.setSuccess(instance!=null);
                result.setMessage(result.isSuccess() ? "发起成功！" : "发起失败!");
                try {
                    String name = String.format("%s的%s流程实例",user.getUserName(),instance.getProcessDefinitionName());
                    managementService.executeCommand(new SetProcessInstanceNameCmd(instance.getProcessInstanceId(),name));
                } catch (Exception e) {
                    log.error("发起流程设置流程实例名称异常",e);
                }
            }catch (Exception e){
                log.error("发起流程出错",e);
                result.setMessage("发起失败!");
            }
        } catch (BusinessException e) {
            result.setMessage(e.getMessage());
        }
        return result;
    }

    /**
     * 通过部署ID删除流程定义
     * @param deploymentId 部署ID
     * @param cascade ture删除启动的流程定义；false未启动的流程定义 默认false
     * @return
     */
    @RequestMapping("deleteByDeploymentId.do")
    @ResponseBody
    public ResourceResult deleteByDeploymentId(String deploymentId, @RequestParam(required = false) Boolean cascade) {
        ResourceResult result = new ResourceResult();
        if (StringUtils.isEmpty(deploymentId))
            result.setMessage( "删除失败!deploymentId参数为空！");
        if (StringUtil.isEmpty(result.getMessage())) {
            try {
                processService.deleteProcessDefinition(deploymentId,cascade);
                result.setSuccess(true);
                result.setMessage("删除成功！");
            }catch (Exception e){
                result.setMessage("删除失败!信息："+e.getMessage());
            }

        }
        return result;
    }
    /**
     * 通过流程定义ID查看流程图
     * @param processDefinitionId 流程定义ID
     * @param response
     */
    @RequestMapping("graph.htm")
    public void showGraphById(@RequestParam(required = true) String processDefinitionId, HttpServletResponse response){
        try {
            response.setContentType("image/png");
            Command<InputStream> cmd= new GetDeploymentProcessDiagramCmd(processDefinitionId);
            if (cmd != null) {
                InputStream is = managementService.executeCommand(cmd);
                IOUtils.copy(is,response.getOutputStream());
            }
        }catch (IOException e){
            log.error("显示流程图出错",e);
        }
    }

}
